# LeetCode 684、冗余连接

# 一、题目描述

树可以看成是一个连通且 无环无向 图。

给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edgesedges[i] = [ai, bi] 表示图中在 aibi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。

示例 1:

img

img

输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]

示例 2:

img

img

输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]

提示:

  • n == edges.length
  • 3 <= n <= 1000
  • edges[i].length == 2
  • 1 <= ai < bi <= edges.length
  • ai != bi
  • edges 中无重复元素
  • 给定的图是连通的

# 二、题目解析

# 三、参考代码

# 1、Java 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 微信:278166530
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
class Solution {

    // 创建二维数组表示单边数组
    List<List<Integer>> graph;

    // 记录从某个点出发开始访问时,每个节点是否被访问
    List<Boolean> visited;

    // 当前是否找到环
    boolean hasCycle = false;

    public int[] findRedundantConnection(int[][] edges) {
        // n 个节点,节点值在 1 ~ n
        int n = edges.length;

        // 初始化图,由于节点值在 1 ~ n,所以二维矩阵的大小为 n + 1
        // 即索引为 0 的那一行不会记录数据
        graph = new ArrayList<List<Integer>>(n + 1);

        // 初始化每一行
        // 每一行记录一个节点的信息
        for (int i = 0; i <= n; i++) {
            // 使用数组进行记录
            graph.add(new ArrayList<Integer>());
        }

        // 访问 edges
        for (int[] edge : edges) {
            // 每一个 edge 都包含了两个节点的信息
            // 由于是无向图,所以两者都可以相互访问到对方
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);

            // 每增一条 edge 边,在原来的已经访问节点的基础上看这条边是否形成了环
            hasCycle = false;

            // 重新初始化 visited
            visited = new ArrayList<Boolean>();

            // 默认都是 false
            for (int i = 0; i <= n; i++) {
                visited.add(false);
            }

            // 开始以 edge[0] 这个节点作为初始节点进行访问
            // 既然以它作为初始节点,那么它的父节点就是不存在的,用 -1 来表示
            dfs(edge[0], -1);

            // 如果在访问结束后发现找到了环
            if(hasCycle){
                // 那么 edge 就是最后出现的边
                return edge;
            }
        }
        return null;
    }

    // 图深度优先遍历
    // curNode :出发节点
    // fatherNode : 出发节点的父节点
    private void dfs(int curNode, int fatherNode) {
        // curNode 这个节点被访问了,更新为 true
        // visited 的索引值就是节点值
        visited.set(curNode, true);

        // 获取 curNode 可以前往的所有节点信息
        List<Integer> info = graph.get(curNode);

        // 对每个可以前往的节点都访问一下
        for (int nextNode : info) {
            
            // 如果发现是前往父节点,不处理,直接看下一个节点
            if (nextNode == fatherNode)
                continue;

            // 如果发现 nextNode 是还未访问的节点
            if (!visited.get(nextNode)) {

                // 那么以 nextNode 作为初始节点,curNode 作为父节点
                // 递归的进行搜索访问下去
                dfs(nextNode, curNode);
            
            // 否则,如果发现 nextNode 是已经访问过的节点
            } else {

                // 说明从 nextNode 经过一系列访问后来到了 curNode
                // curNode 又能来到 nextNode
                // 有环了
                hasCycle = true;
                return;
            }
        }
    }
}

# **2、**C++ 代码


# 3、Python 代码

登录 AlgoMooc 官网获取更多算法图解
# https://www.algomooc.com
# 作者:程序员吴师兄
# 微信:278166530
# 代码有看不懂的地方一定要私聊咨询吴师兄呀
class Solution:
    # 创建二维数组表示单边数组
    graph = []

    # 记录从某个点出发开始访问时,每个节点是否被访问
    visited = []

    # 当前是否找到环
    hasCycle = False

    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        # n 个节点,节点值在 1 ~ n
        n = len(edges)

        # 初始化图,由于节点值在 1 ~ n,所以二维矩阵的大小为 n + 1
        # 即索引为 0 的那一行不会记录数据
        

        # 初始化每一行
        # 每一行记录一个节点的信息
        self.graph = [[] for i in range( n + 1)]

        # 访问 edges
        for edge in edges : 
            # 每一个 edge 都包含了两个节点的信息
            # 由于是无向图,所以两者都可以相互访问到对方
            self.graph[edge[0]].append(edge[1])
            self.graph[edge[1]].append(edge[0])

            # 每增一条 edge 边,在原来的已经访问节点的基础上看这条边是否形成了环
            self.hasCycle = False

            # 重新初始化 visited
            self.visited = [ False ] * (n + 1)

            # 默认都是 false


            # 开始以 edge[0] 这个节点作为初始节点进行访问
            # 既然以它作为初始节点,那么它的父节点就是不存在的,用 -1 来表示
            self.dfs(edge[0], -1)

            # 如果在访问结束后发现找到了环
            if self.hasCycle : 
                # 那么 edge 就是最后出现的边
                return edge
  

    # 图深度优先遍历
    # curNode :出发节点
    # fatherNode : 出发节点的父节点
    def dfs(self,curNode, fatherNode) :
        # curNode 这个节点被访问了,更新为 true
        # visited 的索引值就是节点值
        self.visited[curNode] = True

        # 获取 curNode 可以前往的所有节点信息

        # 对每个可以前往的节点都访问一
        for nextNode in self.graph[curNode]:
            
            # 如果发现是前往父节点,不处理,直接看下一个节点
            if nextNode == fatherNode : 
                continue

            # 如果发现 nextNode 是还未访问的节点
            if self.visited[nextNode] == False:

                # 那么以 nextNode 作为初始节点,curNode 作为父节点
                # 递归的进行搜索访问下去
                self.dfs(nextNode, curNode)
            
            # 否则,如果发现 nextNode 是已经访问过的节点
            else:

                # 说明从 nextNode 经过一系列访问后来到了 curNode
                # curNode 又能来到 nextNode
                # 有环了
                self.hasCycle = True
                return